package com.guchenbo.example.thread;

/**
 * @Author guchenbo
 * @Date 2018/10/21.
 */
public class Wait {
    /**
     * wait()
     *  1、使当前线程等待
     *  2、释放当前线程获取的同步锁
     */
    /**
     * notify()/notifyAll()
     * 1、唤醒方法调用对象上的同步锁关联的等待线程
     */

    public static void main(String[] args) {
        Wait wait = new Wait();
        wait.test1();
//        wait.test2();
//        wait.test3();
//        wait.test4();

    }

    private void success() {
        synchronized (this) {
            try {
                Object obj = new Object();
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void error1() {
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void error2() {
        synchronized (this) {
            try {
                Object obj = new Object();
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 第三方对象作为锁
     */
    private void test1() {
        Thread lock = new Thread();
        ThreadA t1 = new ThreadA(lock);
        Thread t = new Thread(t1);
        t.setName("t1");

        // main线程和t1线程共用同一把锁
        synchronized (lock) {
            // main线程先获取锁，然后启动t1，这样t1，肯定获取锁失败，需要等到main释放
            t.start();
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + " wait ");
                lock.wait();
                System.out.println(Thread.currentThread().getName() + " continue ");

                System.out.println(t.getState());
                System.out.println(lock.getState());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 使用wait方法调用者的锁
     */
    private void test2() {
        ThreadB t1 = new ThreadB(this);
        Thread t = new Thread(t1);
        t.setName("thread-b");

        synchronized (this) {
            t.start();
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + " wait ");
                wait();
                System.out.println(Thread.currentThread().getName() + " continue ");

                System.out.println(t.getState());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 使用notify方法调用者的锁
     */
    private void test3() {
        ThreadC t1 = new ThreadC();
        Thread t = new Thread(t1);
        t.setName("thread-c");

        synchronized (t1) {
            t.start();
            try {
                System.out.println(Thread.currentThread().getName() + " wait ");
                t1.wait();
                System.out.println(Thread.currentThread().getName() + " continue ");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void test4() {
        ThreadD t1 = new ThreadD();
        t1.setName("thread-d");

        //        t1.start();
        System.out.println(t1.getName() + " status " + t1.getState() + ", alive " + t1.isAlive());

        synchronized (t1) {
            t1.start();
            try {
                System.out.println(Thread.currentThread().getName() + " wait ");
                t1.wait();

                System.out.println(t1.getName() + " status " + t1.getState() + ", alive " + t1.isAlive());
                System.out.println(Thread.currentThread().getName() + " continue ");

                //                                t1.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class ThreadA implements Runnable {

        private Thread lock;

        public ThreadA(Thread lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " start ");
            System.out.println(Thread.currentThread().getName() + " 尝试获取锁 ");
            // 需要获取obj的锁才能运行
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " 获取锁成功 ");
                System.out.println(Thread.currentThread().getName() + " call notify");
                lock.notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadB implements Runnable {
        private Object lock;

        public ThreadB(Object lock) {
            this.lock = lock;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " start ");
            // 需要获取obj的锁才能运行
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " call notify");
                lock.notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadC implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " start ");
            // 需要获取obj的锁才能运行
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " call notify");
                //                notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private class ThreadD extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " start2 ");
            // 需要获取obj的锁才能运行
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " call notify");
                //                notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
